#include "netclient.h"
#include "../model/datacenter.h"
#include "../model/data.h"

namespace network
{
    NetClient::NetClient(model::DataCenter *dataCenter)
        :_dataCenter(dataCenter)
    {
        initWebSocket();
    }

    void NetClient::handleWsResponse(const LYZ::NotifyMessage &notifyMessage)
    {
        if (notifyMessage.notifyType() == LYZ::NotifyTypeGadget::NotifyType::CHAT_MESSAGE_NOTIFY)
        {
            // 收到消息
            model::MessageInfo message;
            message.load(notifyMessage.newMessageInfo().messageInfo());
            handleWsMessage(message);
        }
        else if (notifyMessage.notifyType() == LYZ::NotifyTypeGadget::NotifyType::CHAT_SESSION_CREATE_NOTIFY)
        {
            // 创建会话
        }
        else if (notifyMessage.notifyType() == LYZ::NotifyTypeGadget::NotifyType::FRIEND_ADD_APPLY_NOTIFY)
        {
            // 好友申请
        }
        else if (notifyMessage.notifyType() == LYZ::NotifyTypeGadget::NotifyType::FRIEND_ADD_PROCESS_NOTIFY)
        {
            // 好友申请处理
        }
        else if (notifyMessage.notifyType() == LYZ::NotifyTypeGadget::NotifyType::FRIEND_REMOVE_NOTIFY)
        {
            // 好友删除
        }
        else
        {
            ERROR() << "未知类型通知: " << notifyMessage.notifyType();
        }
    }

    void NetClient::handleWsMessage(const model::MessageInfo &message)
    {
        QList<model::MessageInfo> *messageList = _dataCenter->getRecentMsg(message._chat_session_id);
        if (messageList == nullptr)
        {
            // 如果当前这个消息所属的会话, 里面的消息列表没有在本地加载, 需要在网络中加载会话的消息列表
            connect(_dataCenter, &model::DataCenter::getRecentMsgNoUiDone, this, &NetClient::receiveMessage, Qt::UniqueConnection);
            _dataCenter->getRecentMsgAsync(message._chat_session_id, false);
        }
        else
        {
            // 否则进行尾插
            messageList->push_back(message);
            receiveMessage(message._chat_session_id, message);
        }
    }

    void NetClient::ping()
    {
        QNetworkRequest httpReq;
        httpReq.setUrl(QUrl(HTTP_URL + "/ping"));

        QNetworkReply *httpRsp = _httpClient.get(httpReq);

        connect(httpRsp, &QNetworkReply::finished, this, [=](){
            if (httpRsp->error() != QNetworkReply::NoError)
            {
                ERROR() << "HTTP 请求失败: " << httpRsp->errorString();
                return;
            }
            QByteArray body = httpRsp->readAll();
            DEBUG() << "响应内容: " << body;
            httpRsp->deleteLater();
        });
    }

    void NetClient::initWebSocket()
    {
        // 准备好信号槽
        connect(&_webSocketClient, &QWebSocket::connected, this, [=](){
            DEBUG() << "websocket 连接成功";
            sendAuth();
        });

        connect(&_webSocketClient, &QWebSocket::disconnected, this, [=](){
            DEBUG() << "websocket 连接关闭";
        });

        connect(&_webSocketClient, &QWebSocket::errorOccurred, this, [=](){
            DEBUG() << "websocket 连接错误";
        });

        connect(&_webSocketClient, &QWebSocket::textMessageReceived, this, [=](const QString &msg){
            DEBUG() << "websocket 收到文本消息: " << msg;
        });

        connect(&_webSocketClient, &QWebSocket::binaryMessageReceived, this, [=](const QByteArray &msg){
            DEBUG() << "websocket 收到二进制消息: " << msg.length();
            LYZ::NotifyMessage notifyMessage;
            notifyMessage.deserialize(&_serializer, msg);
            handleWsResponse(notifyMessage);
        });

        // 和服务器建立连接
        _webSocketClient.open(WEBSOCKET_URL);
    }

    void NetClient::sendAuth()
    {
        LYZ::ClientAuthenticationReq req;
        req.setRequestId(makeRequestId());
        req.setSessionId(_dataCenter->getLoginSessionId());
        QByteArray body = req.serialize(&_serializer);
        _webSocketClient.sendBinaryMessage(body);
        DEBUG() << "[ws身份认证] requestId: " << req.requestId() << ", loginSessionId: " << req.sessionId();
    }

    QNetworkReply *NetClient::sendHttpReq(const QString &apiPath, const QByteArray &body)
    {
        QNetworkRequest httpReq;
        httpReq.setUrl(QUrl(HTTP_URL + apiPath));
        httpReq.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-protobuf");

        QNetworkReply *httpRsp = _httpClient.post(httpReq, body);
        return httpRsp;
    }

    // 获取当前用户的信息
    void NetClient::getMyself(const QString &loginSessionId)
    {
        // 构造出请求的body部分
        LYZ::GetUserInfoReq req;
        req.setRequestId(makeRequestId());
        req.setSessionId(loginSessionId);
        QByteArray body = req.serialize(&_serializer);
        DEBUG() << "[获取个人信息] 发送请求 requestId: " << req.requestId() << ", loginSessionId: " << req.sessionId();

        // 构造出http请求并发送出去
        QNetworkReply *httpRsp = sendHttpReq("/service/user/get_user_info", body);

        // 通过信号槽获取当前的响应
        connect(httpRsp, &QNetworkReply::finished, this, [=](){
            bool ok = true;
            QString reason;
            auto rsp = handleHttpRsp<LYZ::GetUserInfoRsp>(httpRsp, ok, reason);

            if (!ok)
            {
                ERROR() << "[获取个人信息] 失败 requestId: " << req.requestId() << ", reason: " << reason;
                return;
            }

            _dataCenter->resetMyself(rsp);

            emit _dataCenter->getMyselfDone();

            DEBUG() << "[获取个人信息] 响应处理完毕 requestId: " << req.requestId();
        });
    }

    // 获取好友列表
    void NetClient::getFriendList(const QString &loginSessionId)
    {
        LYZ::GetFriendListReq req;
        req.setRequestId(makeRequestId());
        req.setSessionId(loginSessionId);
        QByteArray body = req.serialize(&_serializer);
        DEBUG() << "[获取好友列表] 发送请求 requestId: " << req.requestId() << ", loginSessionId: " << req.sessionId();

        QNetworkReply *httpRsp = sendHttpReq("/service/friend/get_friend_list", body);

        connect(httpRsp, &QNetworkReply::finished, this, [=](){
            bool ok = true;
            QString reason;
            auto rsp = handleHttpRsp<LYZ::GetFriendListRsp>(httpRsp, ok, reason);

            if (!ok)
            {
                ERROR() << "[获取好友列表] 失败 requestId: " << req.requestId() << ", reason: " << reason;
                return;
            }

            _dataCenter->resetFriendList(rsp);

            emit _dataCenter->getFriendListDone();

            DEBUG() << "[获取好友列表] 响应处理完毕 requestId: " << req.requestId();
        });
    }

    // 获取会话列表
    void NetClient::getChatSessionList(const QString &loginSessionId)
    {
        LYZ::GetChatSessionListReq req;
        req.setRequestId(makeRequestId());
        req.setSessionId(loginSessionId);
        QByteArray body = req.serialize(&_serializer);
        DEBUG() << "[获取会话列表] 发送请求 requestId: " << req.requestId() << ", loginSessionId: " << req.sessionId();

        QNetworkReply *httpRsp = sendHttpReq("/service/friend/get_chat_session_list", body);

        connect(httpRsp, &QNetworkReply::finished, this, [=](){
            bool ok = true;
            QString reason;
            auto rsp = handleHttpRsp<LYZ::GetChatSessionListRsp>(httpRsp, ok, reason);

            if (!ok)
            {
                ERROR() << "[获取会话列表] 失败 requestId: " << req.requestId() << ", reason: " << reason;
                return;
            }

            _dataCenter->resetChatSessionList(rsp);

            emit _dataCenter->getChatSessionListDone();

            DEBUG() << "[获取会话列表] 响应处理完毕 requestId: " << req.requestId();
        });
    }

    // 获取申请列表
    void NetClient::getApplyList(const QString &loginSessionId)
    {
        LYZ::GetPendingFriendEventListReq req;
        req.setRequestId(makeRequestId());
        req.setSessionId(loginSessionId);
        QByteArray body = req.serialize(&_serializer);
        DEBUG() << "[获取申请列表] 发送请求 requestId: " << req.requestId() << ", loginSessionId: " << req.sessionId();

        QNetworkReply *httpRsp = sendHttpReq("/service/friend/get_pending_friend_events", body);

        connect(httpRsp, &QNetworkReply::finished, this, [=](){
            bool ok = true;
            QString reason;
            auto rsp = handleHttpRsp<LYZ::GetPendingFriendEventListRsp>(httpRsp, ok, reason);

            if (!ok)
            {
                ERROR() << "[获取申请列表] 失败 requestId: " << req.requestId() << ", reason: " << reason;
                return;
            }

            _dataCenter->resetApplyList(rsp);

            emit _dataCenter->getApplyListDone();

            DEBUG() << "[获取会话列表] 响应处理完毕 requestId: " << req.requestId();
        });
    }

    void NetClient::getRecentMsg(const QString &loginSessionId, const QString &chatSessionId, bool updateUI = true)
    {
        LYZ::GetRecentMsgReq req;
        req.setRequestId(makeRequestId());
        req.setChatSessionId(chatSessionId);
        req.setMsgCount(50);
        req.setSessionId(loginSessionId);
        QByteArray body = req.serialize(&_serializer);
        DEBUG() << "[获取会话最近消息] 发送请求 requestId: " << req.requestId() << ", loginSessionId: " << req.sessionId();

        QNetworkReply *httpRsp = sendHttpReq("/service/message_storage/get_recent", body);

        connect(httpRsp, &QNetworkReply::finished, this, [=](){
            bool ok = true;
            QString reason;
            auto rsp = handleHttpRsp<LYZ::GetRecentMsgRsp>(httpRsp, ok, reason);

            if (!ok)
            {
                ERROR() << "[获取会话最近消息] 失败 requestId: " << req.requestId() << ", reason: " << reason;
                return;
            }

            _dataCenter->resetRecentMsgList(chatSessionId, rsp);
            model::MessageInfo msg;
            msg.load(rsp->msgList().back());
            if (updateUI)
                emit _dataCenter->getRecentMsgDone(chatSessionId);
            else
                emit _dataCenter->getRecentMsgNoUiDone(chatSessionId, msg);

            DEBUG() << "[获取会话列表] 响应处理完毕 requestId: " << req.requestId();
        });
    }

    void NetClient::sentMessage(const QString &loginSessionId, const QString &chatSessionId,
                                model::MessageType messageType, const QByteArray &content, const QString &extraInfo)
    {
        LYZ::NewMessageReq req;
        req.setRequestId(makeRequestId());
        req.setChatSessionId(chatSessionId);
        req.setSessionId(loginSessionId);

        LYZ::MessageContent msgCon;
        if (messageType == model::MessageType::STRING_TYPE)
        {
            msgCon.setMessageType(LYZ::MessageTypeGadget::MessageType::STRING);

            LYZ::StringMessageInfo strMsgInfo;
            strMsgInfo.setContent(content);
            msgCon.setStringMessage(strMsgInfo);
        }
        else if (messageType == model::MessageType::IMAGE_TYPE)
        {
            msgCon.setMessageType(LYZ::MessageTypeGadget::MessageType::IMAGE);

            LYZ::ImageMessageInfo imageMsgInfo;
            imageMsgInfo.setFileId("");
            imageMsgInfo.setImageContent(content);
            msgCon.setImageMessage(imageMsgInfo);
        }
        else if (messageType == model::MessageType::FILE_TYPE)
        {
            msgCon.setMessageType(LYZ::MessageTypeGadget::MessageType::FILE);

            LYZ::FileMessageInfo fileMsgInfo;
            fileMsgInfo.setFileId("");
            fileMsgInfo.setFileContents(content);
            fileMsgInfo.setFileSize(content.size());
            fileMsgInfo.setFileName(extraInfo);
            msgCon.setFileMessage(fileMsgInfo);
        }
        else if (messageType == model::MessageType::SPEECH_TYPE)
        {
            msgCon.setMessageType(LYZ::MessageTypeGadget::MessageType::SPEECH);

            LYZ::SpeechMessageInfo speechMsgInfo;
            speechMsgInfo.setFileId("");
            speechMsgInfo.setFileContents(content);
            msgCon.setSpeechMessage(speechMsgInfo);
        }
        else
        {
            ERROR() << "消息类型错误 type: " << messageType;
            return;
        }
        req.setMessage(msgCon);

        QByteArray body = req.serialize(&_serializer);
        DEBUG() << "[发送文本消息] 发送请求 requestId: " << req.requestId() << ", loginSessionId: " << req.sessionId();

        QNetworkReply *httpRsp = sendHttpReq("/service/transmit/new_message", body);

        connect(httpRsp, &QNetworkReply::finished, this, [=](){
            bool ok = true;
            QString reason;
            auto rsp = handleHttpRsp<LYZ::NewMessageRsp>(httpRsp, ok, reason);

            if (!ok)
            {
                ERROR() << "[发送文本消息] 失败 requestId: " << req.requestId() << ", reason: " << reason;
                return;
            }

            emit _dataCenter->sendMessageDone(messageType, content, extraInfo);

            DEBUG() << "[发送文本消息] 响应处理完毕 requestId: " << req.requestId();
        });
    }

    void NetClient::receiveMessage(const QString &chatSessionId, const model::MessageInfo &message)
    {
        DEBUG() << "receiveMessaeg: chatSessionId: " << chatSessionId;
        DEBUG() << "currentSession: " << _dataCenter->getCurrentSessionId();
         _dataCenter->updateChatSessionLastMsg(chatSessionId, message);
        if (chatSessionId == _dataCenter->getCurrentSessionId())
        {
            // 收到消息的会话是当前选中的会话
            emit _dataCenter->receiveMessageDone(message);
        }
        else
        {
            // 不是当前选中会话就更新未读消息
            _dataCenter->addUnread(chatSessionId);
        }
        emit _dataCenter->updateLastMsg(chatSessionId);
    }

    void NetClient::changeNickname(const QString &loginSessionId, const QString &newName)
    {
        LYZ::SetUserNicknameReq req;
        req.setRequestId(makeRequestId());
        req.setSessionId(loginSessionId);
        req.setNickname(newName);
        QByteArray body = req.serialize(&_serializer);
        DEBUG() << "[设置用户昵称] 发送请求 requestId: " << req.requestId() << ", loginSessionId: " << req.sessionId();

        QNetworkReply *httpRsp = sendHttpReq("/service/user/set_nickname", body);

        connect(httpRsp, &QNetworkReply::finished, this, [=](){
            bool ok = true;
            QString reason;
            auto rsp = handleHttpRsp<LYZ::SetUserNicknameRsp>(httpRsp, ok, reason);

            if (!ok)
            {
                ERROR() << "[设置用户昵称] 失败 requestId: " << req.requestId() << ", reason: " << reason;
                return;
            }

            _dataCenter->resetNickname(newName);

            emit _dataCenter->changeNicknameDone();

            DEBUG() << "[设置用户昵称] 响应处理完毕 requestId: " << req.requestId();
        });
    }

    QString NetClient::makeRequestId()
    {
        return model::makeId("R");
    }
}
